package com.smm.web.vmc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smm.web.vmc.dao.BaseDao;
import com.smm.web.vmc.domain.*;
import com.smm.web.vmc.domain.relation.CameraReceiptRelation;
import com.smm.web.vmc.domain.relation.UserReceiptRelation;
import com.smm.web.vmc.dto.AlamQueryDto;
import com.smm.web.vmc.dto.ReceiptDto;
import com.smm.web.vmc.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.BeanUtils;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by zhenghao on 2016/8/31.
 *
 */
@Slf4j
@Service
public class ReceiptServiceImpl implements ReceiptService {

    @Resource
    private BaseDao<UserReceiptRelation> userReceiptRelationBaseDao;

    @Resource
    private BaseDao<CameraReceiptRelation> cameraReceiptRelationBaseDao;

    @Resource
    private BaseDao<Receipt> receiptBaseDao;

    @Resource
    private BaseDao<ReceiptClose> receiptCloseBaseDao;

    @Resource
    private WarehouseService warehouseService;

    @Resource
    private CameraService cameraService;

    @Resource
    private CameraReceiptRelationService cameraReceiptRelationService;

    @Resource
    private UserReceiptRelationService userReceiptRelationService;

    @Resource
    private TagService tagService;

    @Resource
    private AlamService alamService;


    @Override
    public List<ReceiptDto> getAllByUser(int userId) {

        //获取该用户的所有仓单列表
        List<Receipt> receipts  = getReceiptsByUser(userId);

        if(receipts==null || receipts.size()==0) return null;

        //获取所有仓单编号
        List<String> receiptNos = receipts.stream().map(Receipt::getNo).collect(Collectors.toList());

        //获取所有仓单关联的摄像头列表，查询结果按仓单编号分组
        DetachedCriteria where = DetachedCriteria.forClass(CameraReceiptRelation.class);
        where.add(Restrictions.in("receipt.no",receiptNos));
        List<CameraReceiptRelation> cameraReceiptRelations = cameraReceiptRelationBaseDao.query(where);

        log.info("----------------- cameraReceiptRelations size :" + cameraReceiptRelations);

        //如果摄像头列表不为空，按仓单编号分组
        Map<String,List<CameraReceiptRelation>> tmpMap = null;

        if(cameraReceiptRelations!=null && cameraReceiptRelations.size()>0){

            tmpMap = cameraReceiptRelations.stream().collect(Collectors.groupingBy(x->x.getReceipt().getNo()));
        }

        //定义返回数据
        List<ReceiptDto> receiptDtos = new ArrayList<>();

        //组合仓单与摄像头数据
        for(Receipt receipt:receipts){

            ReceiptDto rdto = new ReceiptDto();

            BeanUtils.copyProperties(receipt,rdto);

            if(tmpMap!=null && tmpMap.get(receipt.getNo())!=null){

                //从仓单摄像头关系集合中抽出摄像头列表
                rdto.setCameraList(tmpMap.get(receipt.getNo()).stream().map(CameraReceiptRelation::getCamera).collect(Collectors.toList()));
            }

            receiptDtos.add(rdto);

        }

        return receiptDtos;
    }

    public List<Receipt> getReceiptsByUser(int userId) {

        log.info("----------------- user Id :" + userId);

        DetachedCriteria where = DetachedCriteria.forClass(UserReceiptRelation.class);
        where.add(Restrictions.eq("user.id", userId));
        List<UserReceiptRelation> receiptRelations = userReceiptRelationBaseDao.query(where);

        if(receiptRelations==null || receiptRelations.size()==0) return null;

        //抽取仓单列表
        List<Receipt> receipts = receiptRelations.stream().map(UserReceiptRelation::getReceipt).collect(Collectors.toList());

        return receipts;
    }

    @Override
    public List<String> countBreed() {

        DetachedCriteria where = DetachedCriteria.forClass(Receipt.class);
        where.setProjection(Projections.groupProperty("breed"));

        HibernateTemplate template = receiptBaseDao.getTemplate();

        List<String> breeds = (List<String>) template.findByCriteria(where);

        return breeds;
    }

    @Override
    public List<Receipt> getReceiptsByQueryAndUser(AlamQueryDto queryDto, int userId) {

        DetachedCriteria where = DetachedCriteria.forClass(UserReceiptRelation.class);
        where.add(Restrictions.eq("user.id", userId));

        //执行查询
        List<UserReceiptRelation> receiptRelations = userReceiptRelationBaseDao.query(where);

        if(receiptRelations==null || receiptRelations.size()==0) return null;

        //抽取仓单列表
        List<Receipt> receipts = receiptRelations.stream().map(UserReceiptRelation::getReceipt).collect(Collectors.toList());

        //获取仓单编号
        List<String> receiptNos = receipts.stream().map(Receipt::getNo).collect(Collectors.toList());


        //如果有查询条件、从仓单对象过滤
        where = DetachedCriteria.forClass(Receipt.class);
        where.add(Restrictions.in("no", receiptNos));

        //如果有品类查询条件
        if(StringUtils.isNotBlank(queryDto.getBreed())){
            where.add(Restrictions.eq("breed", queryDto.getBreed()));
        }

        //如果有仓库编号查询条件
        if(queryDto.getWarehouseId()>0){

            where.add(Restrictions.eq("warehouse.id", queryDto.getWarehouseId()));
        }

        List<Receipt> receiptList = receiptBaseDao.query(where);

        return receiptList;
    }

    @Override
    public Receipt getOneById(String repNo) {

        return receiptBaseDao.getOne(Receipt.class,repNo);
    }

    @Override
    public List<Receipt> getAllByWarehouser(int whId) {

        DetachedCriteria where = DetachedCriteria.forClass(Receipt.class);
        where.add(Restrictions.eq("warehouse.id",whId));
        where.addOrder(Order.desc("id"));

        return receiptBaseDao.query(where);
    }

    @Override
    public List<ReceiptDto> getAllDtoByWarehouser(int whId) {
        String sql = "SELECT receipt.* ,\nCOUNT( DISTINCT camerareceiptrelation.`camera_id`) ,\nCOUNT(DISTINCT tag.`no`),\nCOUNT(DISTINCT alam.`id`) \nFROM receipt\nLEFT JOIN camerareceiptrelation ON camerareceiptrelation.`receipt_no` = receipt.`no`\nLEFT JOIN tag\tON tag.`receipt_no` = receipt.`no`\nLEFT JOIN alam ON alam.`receipt_no` = receipt.`no` where receipt.warehouse_id=" + whId + "  group by receipt.no ";
        List result = this.receiptBaseDao.createSqlQuery(sql);
        return  this.getReceiptDtoListFromResult(result);
    }

    @Override
    @Transactional
    public void add(ReceiptDto receiptDto) throws Exception {

        //保存仓单对象
        Receipt receipt = saveReceipt(receiptDto);

        //保存仓单与摄像头关联关系
        String condition = receiptDto.getJsonData();

        List<Integer> cameraIdList = getCameraIdsFromCondition(condition);

        saveReceiptWithCamera(receipt,cameraIdList);

        //保存仓单与tag 关联关系 ，同时保存 tag 与摄像头关联关系， 如果有
        List<TagWithCamera> tcList = getTagFromCondition(condition);

        saveReceiptWithTag(receipt,tcList);

    }

    @Override
    @Transactional
    public void update(ReceiptDto receiptDto) throws Exception {

        log.info("获取仓单对象");

        //获取原来仓单对象
        Receipt receipt = receiptBaseDao.getOne(Receipt.class,receiptDto.getNo());

        Warehouse warehouse = receipt.getWarehouse();

        log.info("复制仓单表单数据");
        //更新仓单信息
        BeanUtils.copyProperties(receiptDto,receipt);

        receipt.setWarehouse(warehouse);

        log.info("更新仓单数据");
        receiptBaseDao.update(receipt);

        log.info("删除仓单余摄像头关系");

        //删除原仓单与摄像头关联关系
        cameraReceiptRelationService.deleteAllByReceipt(receipt.getNo());

        //新增新的仓单与摄像头关联关系
        String condition = receiptDto.getJsonData();

        List<Integer> cameraIdList = getCameraIdsFromCondition(condition);

        log.info("新增仓单余摄像头关系");
        saveReceiptWithCamera(receipt,cameraIdList);

        //更新tag 信息. 删除标签原来与摄像头、仓单的关系， 保存新的关联关系
        List<TagWithCamera> tcList = getTagFromCondition(condition);

        log.info("更新仓单余标签关联关系");
        updateReceiptWithTag(receipt, tcList);

    }

    private void updateReceiptWithTag(Receipt receipt, List<TagWithCamera> newTagList) throws Exception {

        //获取原来的关联关系
        List<Tag> tagList = tagService.getAllByReceipt(receipt.getNo());

        //如果同时为空， 直接返回
        if(newTagList==null || newTagList.size()==0){

            if(tagList==null || tagList.size()==0) return;

        }

        //如果新关系为空， 清空所有老关系
        if(newTagList==null || newTagList.size()==0) {

            for(Tag tag:tagList){

                this.cleanTagRenation(tag);
            }
            return;
        }

        //如果老关系为空，新增所有新关系
        if(tagList==null || tagList.size()==0){

            addTagRenationByTagWithCameraList(receipt,newTagList);
            return;
        }


        //如果都不为空， 找出新增 、 删除的部分。
        List<Tag> removeTagList = getRemoveTagList(tagList,newTagList);

        if(removeTagList!=null && removeTagList.size()>0){

            for(Tag tag : removeTagList){

                this.cleanTagRenation(tag);
            }
        }


        //新增的关联关系
        List<TagWithCamera> addTagList = getAddTagList(tagList,newTagList);

        if(addTagList!=null && addTagList.size()>0){

            addTagRenationByTagWithCameraList(receipt,addTagList);
        }

    }


    //清空关联关系
    private void cleanTagRenation(Tag tag) throws Exception {

        tag.setReceipt(null);
        tag.setCamera(null);
        this.tagService.update(tag);
    }

    //新增关联关系
    private void addTagRenationByTagWithCameraList(Receipt receipt, List<TagWithCamera> newTagList) throws Exception {

        if(newTagList==null || newTagList.size()==0) return;

        if(receipt==null) return;

        for(TagWithCamera tc : newTagList){

            Tag newTag = new Tag();
            newTag.setNo(tc.getTagNo());
            newTag.setReceipt(receipt);
            newTag.setCamera(this.cameraService.getOneById(tc.getCameraId()));
            newTag.setWarehouse(receipt.getWarehouse());
            tagService.update(newTag);

        }
    }


    //找出新list中， 老list 没有的部分
    private List<TagWithCamera> getAddTagList(List<Tag> tagList, List<TagWithCamera> newTagList) {

        List<TagWithCamera> resultList = new ArrayList<>();

        for(TagWithCamera tc:newTagList){

            boolean in = false;

            for(Tag tag:tagList){

                in = tag.getNo().equals(tc.getTagNo());

                if(in) break;
            }

            if(!in) resultList.add(tc);
        }

        return resultList;


    }

    //找出老list 中， 新list 没有的部分
    private List<Tag> getRemoveTagList(List<Tag> tagList, List<TagWithCamera> newTagList) {

        List<Tag> resultList = new ArrayList<>();

        for(Tag tag:tagList){

            boolean in = false;

            for(TagWithCamera tc:newTagList){

                in = tag.getNo().equals(tc.getTagNo());

                if(in) break;
            }

            if(!in) resultList.add(tag);
        }

        return resultList;
    }

    //检查标签编号是否在集合中存在. 存在，返回 tag 对象， 否则返回 null
    private Tag checkInclude(String tagNo, List<Tag> tagList) {

        if(tagList==null || tagList.size()==0) return null;

        for (Tag tag : tagList){

            if(tag.getNo().equals(tagNo)) return tag;
        }

        return null;
    }

    @Override
    public void delete(String repNo) throws Exception {

        //检查是否有关联摄像头，如果有，不能删除
        List<CameraReceiptRelation> relations = cameraReceiptRelationService.getAllByReceipt(repNo);

        if(relations!=null && relations.size()>0) throw new Exception("有关联的摄像头，不能删除");

        //检查是否有关联tag，如果有不能删除
        List<Tag> tags = tagService.getAllByReceipt(repNo);

        if(tags!=null && tags.size()>0) throw new Exception("有关联的RFID，不能删除");

        //执行删除
        receiptBaseDao.delete(receiptBaseDao.getOne(Receipt.class,repNo));
    }

    @Override
    public List<Receipt> getAll() {

        DetachedCriteria where = DetachedCriteria.forClass(Receipt.class);

        where.addOrder(Order.desc("joinTime"));

        return receiptBaseDao.query(where);
    }

    @Override
    @Transactional
    public void close(String repNo) throws Exception {

        //删除仓单摄像头关联
        this.cameraReceiptRelationService.deleteAllByReceipt(repNo);

        //删除仓单标签关联
        this.tagService.deleteAllByReceipt(repNo);

        //删除仓单用户关联
        this.userReceiptRelationService.deleteByReceipt(repNo);

        //删除关联的报警记录
        this.alamService.deleteAllByReceipt(repNo);

        //复制仓单数据
        Receipt receipt = this.getOneById(repNo);

        ReceiptClose close = new ReceiptClose();

        BeanUtils.copyProperties(receipt,close);

        close.setCloseTime(new Date());

        //新增已关闭仓单
        this.receiptCloseBaseDao.add(close);

        //删除仓单
        this.receiptBaseDao.delete(receipt);
    }

    @Override
    public List<ReceiptDto> getAllDto(Pager pager) {

        //统计数量
        String countSql=" select count(no) from receipt ";

        List countResult = this.receiptBaseDao.createSqlQuery(countSql);

        BigInteger obj = (BigInteger) countResult.get(0);

        pager.setRowCount(obj.intValue());

        //分页查询
        String sql = "SELECT receipt.* ,\nCOUNT( DISTINCT camerareceiptrelation.`camera_id`) ,\nCOUNT(DISTINCT tag.`no`),\nCOUNT(DISTINCT alam.`id`) \n " +
                "FROM receipt\nLEFT JOIN camerareceiptrelation ON camerareceiptrelation.`receipt_no` = receipt.`no`\nLEFT JOIN tag\tON tag.`receipt_no` = receipt.`no`\n " +
                "LEFT JOIN alam ON alam.`receipt_no` = receipt.`no` group by receipt.no limit "+pager.firstResultIndex()+","+ pager.getPageSize();

        List result = this.receiptBaseDao.createSqlQuery(sql);

        return getReceiptDtoListFromResult(result);

    }

    @Override
    public List<ReceiptDto> searchAllDtoByNo(String no, Pager pager) {

        //统计数量
        String countSql=" select count(no) from receipt where no like '%"+no.trim()+"%' ";

        List countResult = this.receiptBaseDao.createSqlQuery(countSql);

        BigInteger obj = (BigInteger) countResult.get(0);

        pager.setRowCount(obj.intValue());

        //分页查询
        String sql = "SELECT receipt.* ,\nCOUNT( DISTINCT camerareceiptrelation.`camera_id`) ,\nCOUNT(DISTINCT tag.`no`),\nCOUNT(DISTINCT alam.`id`) \n " +
                "FROM receipt\nLEFT JOIN camerareceiptrelation ON camerareceiptrelation.`receipt_no` = receipt.`no`\nLEFT JOIN tag\tON tag.`receipt_no` = receipt.`no`\n " +
                "LEFT JOIN alam ON alam.`receipt_no` = receipt.`no` where receipt.no like '%"+no.trim()+"%'  group by receipt.no limit "+pager.firstResultIndex()+","+ pager.getPageSize();

        List result = this.receiptBaseDao.createSqlQuery(sql);

        return getReceiptDtoListFromResult(result);
    }

    @Override
    public List<ReceiptClose> getAllClosedReceipts() {

        return this.receiptCloseBaseDao.getAll(ReceiptClose.class);
    }

    private List<ReceiptDto> getReceiptDtoListFromResult(List result) {

        if(result==null || result.size()==0) return null;

        List<ReceiptDto> receiptDtoList = new ArrayList<>();

        for (Object aResult : result) {
            Object[] obj = (Object[]) aResult;
            ReceiptDto dto = new ReceiptDto();
            dto.setNo(obj[0].toString());
            dto.setAttribute(obj[1].toString());
            dto.setBreed(obj[2].toString());
            dto.setGoodsQuantity(new BigDecimal(Double.parseDouble(obj[3].toString())));
            dto.setGoodsWeight(new BigDecimal(Double.parseDouble(obj[4].toString())));



            log.info("---:" + obj[5].toString());
            dto.setJoinTime((Date) obj[5]);
            dto.setLocation(obj[6].toString());
            dto.setOwnerName(obj[7].toString());
            dto.setQuantityType(Short.parseShort(obj[8].toString()));
            dto.setWeightType(Short.parseShort(obj[9].toString()));
            dto.setWarehouseId(Integer.parseInt(obj[10].toString()));

            dto.setCameraNumber(Integer.parseInt(obj[12].toString()));
            dto.setTagNumber(Integer.parseInt(obj[13].toString()));
            dto.setAlamNumber(Integer.parseInt(obj[14].toString()));
            receiptDtoList.add(dto);
        }

        return receiptDtoList;
    }


    //内部类，用于对应 tag 与 摄像头的对应关系
    class TagWithCamera{

        private String tagNo;       //标签编号

        private int cameraId;       //摄像头id

        public TagWithCamera(String tagNo, int cameraId) {
            this.tagNo = tagNo;
            this.cameraId = cameraId;
        }

        public String getTagNo() {
            return tagNo;
        }

        public void setTagNo(String tagNo) {
            this.tagNo = tagNo;
        }

        public int getCameraId() {
            return cameraId;
        }

        public void setCameraId(int cameraId) {
            this.cameraId = cameraId;
        }
    }

    private void saveReceiptWithTag(Receipt receipt, List<TagWithCamera> tcList) throws Exception{

        if(tcList==null || tcList.size()==0) return ;

//        for(TagWithCamera twc : tcList){
//
//            Tag tag = new Tag();
//
//            tag.setNo(twc.getTagNo());
//            tag.setReceipt(receipt);
//            tag.setCamera(cameraService.getOneById(twc.getCameraId()));
//            tag.setWarehouse(receipt.getWarehouse());
//
//            tagService.update(tag);
//        }

    }

    private List<TagWithCamera> getTagFromCondition(String condition) throws Exception{

        if(StringUtils.isBlank(condition)) throw new Exception("关联关系为空");

        JSONObject obj = JSON.parseObject(condition);

        JSONArray tags = (JSONArray) obj.get("tags");

        if(tags.isEmpty()) return null;

        List<TagWithCamera> tagWithCameraList = new ArrayList<>();

        for(Object tagItem : tags){

            JSONObject tag = (JSONObject) tagItem;

            TagWithCamera tc= new TagWithCamera(tag.get("no").toString(),Integer.parseInt(tag.get("cameraId").toString()));

            tagWithCameraList.add(tc);

        }

        return tagWithCameraList;
    }

    private void saveReceiptWithCamera(Receipt receipt, List<Integer> cameraIdList) throws Exception{

        if(cameraIdList==null || cameraIdList.size()==0) return ;

        for(Integer id :cameraIdList){

            Camera  camera = cameraService.getOneById(id);

            CameraReceiptRelation crr = new CameraReceiptRelation();

            crr.setCamera(camera);

            crr.setReceipt(receipt);

            cameraReceiptRelationService.add(crr);

        }

    }

    //从传递过来的json 中读取摄像头id
    private List<Integer> getCameraIdsFromCondition(String condition) throws Exception{

        if(StringUtils.isBlank(condition)) throw new Exception("关联关系为空");

        JSONObject obj = JSON.parseObject(condition);

        JSONArray ids = (JSONArray) obj.get("cameraIds");

        if(ids.isEmpty()) return null;

        List<Integer> idList = new ArrayList<>();

        for(Object id :ids){

            idList.add((Integer)id);

        }

        return idList;
    }


    private Receipt saveReceipt(ReceiptDto receiptDto) throws Exception{

        if(receiptDto.getWarehouseId()==0) throw new Exception("仓库编号为空");

        //通过仓库id获取仓单对象
        Warehouse warehouse = warehouseService.getOneById(receiptDto.getWarehouseId());

        if(warehouse==null) throw new Exception("编号为："+receiptDto.getWarehouseId()+" 的仓库记录不存在");

        Receipt receipt = new Receipt();

        //复制属性值
        BeanUtils.copyProperties(receiptDto,receipt);

        receipt.setWarehouse(warehouse);

        this.receiptBaseDao.add(receipt);

        return receipt;
    }


}
